#include<iostream>
#include<vector>
#include<pthread.h>
#include<semaphore.h>
using namespace std;

#define MAX_QUEUE 5
template<class T>
class RingQueue{
private:
    int _capacity;
    int _read_pos;
    int _write_pos; //(write_pos+1)%capacity==_read_pos;
    vector<T> _array;
    sem_t _sem_lock;//这个信号量用于实现锁
    sem_t _sem_idle;//队列空闲空间的计数-对于生产者来说空闲空间就是资源
    sem_t _sem_data;//队列数据空间的计数-对于消费者来说数据就是资源
public:
    RingQueue(int max=MAX_QUEUE)
    :_capacity(max),_read_pos(0),_write_pos(0),_array(_capacity){
        sem_init(&_sem_lock,0,1);
        sem_init(&_sem_idle,0,_capacity);
        sem_init(&_sem_data,0,0);
    }
    ~RingQueue(){
        sem_destroy(&_sem_lock);
        sem_destroy(&_sem_idle);
        sem_destroy(&_sem_data);
    }
    bool Push(const T &data){
        sem_wait(&_sem_idle);//判断有没有空闲空间(有空闲空间，计数减一)
        //锁只保护需要加锁的临界资源
        sem_wait(&_sem_lock);//加锁(计数减一) 
        _array[_write_pos]=data;
        _write_pos=(_write_pos+1)%_capacity;
        sem_post(&_sem_lock);//解锁(计数加一)
        sem_post(&_sem_data);//数据计数+1
        return true;
    }
    bool Pop(T *data){
        sem_wait(&_sem_data);//判断有没有数据(有数据，计数减一)
        sem_wait(&_sem_lock);//加锁(计数减一)
        *data=_array[_read_pos];
        _read_pos=(_read_pos+1)%_capacity;
        sem_post(&_sem_lock);//解锁(计数加一)
        sem_post(&_sem_idle);//空闲空间计数+1
        return true;
    }
};
#define MAX_THREAD 4
void *producer(void *arg){
    RingQueue<int> *bq=(RingQueue<int>*)arg;
    int data=0;
    while(1){
        bq->Push(data);
        //cout内部有缓冲区线程不安全 printf内部有互斥锁是线程安全的
        cout<<pthread_self()<<"push data:"<<data++<<endl;
    }
    return NULL;
}
void *consumer(void *arg){
    RingQueue<int> *bq=(RingQueue<int>*)arg;
    int data=0;
    while(1){
        bq->Pop(&data);
        cout<<pthread_self()<<"push data:"<<data<<endl;
    }
    return NULL;
}
int main(){
    RingQueue<int> bq;
    pthread_t ptid[MAX_THREAD],ctid[MAX_THREAD];
    int ret;
    for(int i=0;i<MAX_THREAD;i++){
        ret=pthread_create(&ptid[i],NULL,producer,&bq);
        if(ret!=0){
            cout<<"create thread fail!\n"<<endl;
            return -1;
        }
        ret=pthread_create(&ctid[i],NULL,consumer,&bq);
        if(ret!=0){
            cout<<"create thread fail!\n"<<endl;
            return -1;
        }
    }
    for(int i=0;i<MAX_THREAD;i++){
        pthread_join(ptid[i],NULL);
        pthread_join(ctid[i],NULL);
    }
    return 0;
}